from fastapi import FastAPI, UploadFile, File, Request, Form
from fastapi.responses import HTMLResponse
import os
import shutil
from tempfile import NamedTemporaryFile
import subprocess
from datetime import datetime
import re
import requests
import json
import configparser
import logging

# ----------------------------
# 导入 OpenAI 和 httpx
# ----------------------------
from openai import OpenAI
import httpx

# ----------------------------
# 配置读取函数
# ----------------------------
def load_config():
    """从 config.ini 加载配置"""
    config = configparser.ConfigParser()
    if not os.path.exists("config.ini"):
        raise FileNotFoundError("配置文件 config.ini 不存在，请确保文件存在。")

    config.read("config.ini", encoding="utf-8")

    return {
        "ffmpeg_path": config.get("Paths", "ffmpeg_path"),
        "output_dir": config.get("Paths", "output_dir"),

        "noise_threshold": config.get("Audio", "noise_threshold"),
        "silence_duration": config.getint("Audio", "silence_duration"),
        "max_segment_duration": config.getint("Audio", "max_segment_duration"),

        "api_url": config.get("API", "api_url"),
        "model_name": config.get("API", "model_name"),
        "api_key": config.get("API", "api_key"),
        "model_name2": config.get("API", "model_name2"),
    }

# ----------------------------
# 全局加载配置
# ----------------------------
try:
    CONFIG = load_config()
    CONFIG["api_endpoint"] = f"{CONFIG['api_url']}/audio/transcriptions"
except Exception as e:
    print(f"加载配置失败: {e}")
    CONFIG = None  # 后续检查

# ----------------------------
# 原有函数（修改为使用 CONFIG）
# ----------------------------

def check_ffmpeg(ffmpeg_path):
    """检查ffmpeg是否存在并可用"""
    if not os.path.exists(ffmpeg_path):
        print(f"FFmpeg 不存在: {ffmpeg_path}")
        return False
    try:
        subprocess.run(
            [ffmpeg_path, "-version"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )
        return True
    except Exception as e:
        print(f"FFmpeg 检查失败: {e}")
        return False


def get_split_points(input_file, config):
    """获取所有可能的分割点（仅分析不切割）"""
    print("正在分析音频，寻找分割点...")

    ffmpeg_cmd = [
        config["ffmpeg_path"], "-i", input_file, "-vn",
        "-af", f"silencedetect=noise={config['noise_threshold']}:d={config['silence_duration']}",
        "-f", "null", "-"
    ]

    result = subprocess.run(
        ffmpeg_cmd,
        stderr=subprocess.PIPE,
        stdout=subprocess.PIPE,
        text=True,
        creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
    )

    silence_pattern = re.compile(r"silence_(start|end): (\d+\.\d+)")
    silence_events = []
    for line in result.stderr.splitlines():
        match = silence_pattern.search(line)
        if match:
            event_type = match.group(1)
            time = float(match.group(2))
            silence_events.append((event_type, time))

    split_points = [0.0]
    for i, (event_type, time) in enumerate(silence_events):
        if event_type == "end" and i > 0 and silence_events[i-1][0] == "start":
            start_time = silence_events[i-1][1]
            if time - start_time >= config["silence_duration"]:
                split_points.append(time)

    filtered_split_points = [0.0]
    for point in split_points[1:]:
        if point - filtered_split_points[-1] > config["max_segment_duration"]:
            filtered_split_points.append(filtered_split_points[-1] + config["max_segment_duration"])
        filtered_split_points.append(point)

    filtered_split_points = sorted(list(set(filtered_split_points)))
    print(f"分析完成，共找到 {len(filtered_split_points)} 个分割点")
    return filtered_split_points


def cut_single_segment(input_file, start_time, end_time, output_file, ffmpeg_path):
    """切割单个音频片段"""
    split_cmd = [
        ffmpeg_path, "-hide_banner", "-loglevel", "error",
        "-i", input_file, "-vn",
        "-ss", str(start_time),
        "-acodec", "libmp3lame", "-q:a", "2"
    ]

    if end_time:
        split_cmd.extend(["-to", str(end_time)])
    split_cmd.append(output_file)

    subprocess.run(
        split_cmd,
        creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
    )
    return os.path.exists(output_file)


def audio_to_text(audio_file_path, config):
    """将音频文件转换为文本"""
    try:
        with open(audio_file_path, 'rb') as audio_file:
            content_type = 'audio/mp3'
            files = {
                'file': (os.path.basename(audio_file_path), audio_file, content_type)
            }

            data = {
                'model': config["model_name"],
                'language': 'auto',
                'response_format': 'text'
            }

            headers = {
                "Authorization": f"Bearer {config['api_key']}"
            }

            print(f"正在识别 {os.path.basename(audio_file_path)}...")
            response = requests.post(
                config["api_endpoint"],
                files=files,
                data=data,
                headers=headers
            )
            response.raise_for_status()

            return response.text

    except Exception as e:
        print(f"识别错误: {str(e)}")
        return None


def process_audio_sequentially(input_file, config):
    """顺序处理音频文件"""
    try:
        if not check_ffmpeg(config["ffmpeg_path"]):
            print(f"错误: 未找到ffmpeg程序，路径：{config['ffmpeg_path']}")
            return None

        os.makedirs(config["output_dir"], exist_ok=True)
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        output_prefix = os.path.join(config["output_dir"], f"segment_{timestamp}_")

        split_points = get_split_points(input_file, config)
        if len(split_points) < 1:
            print("未找到有效的分割点，无法继续处理")
            return None

        transcription_results = []
        combined_text = ""
        segment_count = 1
        total_segments = len(split_points) - 1

        print("开始顺序处理片段...")

        for i in range(len(split_points)):
            start_time = split_points[i]
            end_time = split_points[i+1] if i < len(split_points) - 1 else None

            if end_time and (end_time - start_time < 1.0):
                print(f"跳过太短的片段: {start_time:.2f}s 至 {end_time:.2f}s")
                continue

            progress = (i / total_segments) * 100 if total_segments > 0 else 0
            print(f"进度: {progress:.1f}%")
            logging.info((f"进度: {progress:.1f}%"))
            output_file = f"{output_prefix}{segment_count:03d}.mp3"
            if end_time:
                print(f"切割片段 {segment_count}: {start_time:.2f}s 至 {end_time:.2f}s")
            else:
                print(f"切割片段 {segment_count}: 从 {start_time:.2f}s 开始")

            if not cut_single_segment(input_file, start_time, end_time, output_file, config["ffmpeg_path"]):
                print(f"片段 {segment_count} 切割失败，跳过")
                segment_count += 1
                continue

            text = audio_to_text(output_file, config)

            try:
                if os.path.exists(output_file):
                    os.remove(output_file)
                    print(f"已删除临时文件: {os.path.basename(output_file)}")
            except Exception as e:
                print(f"删除临时文件失败: {str(e)}")

            if text:
                #print(f"片段 {segment_count} 识别成功")
                #print(f"识别结果:\n{text}\n")
                combined_text += text + "\n\n"
                transcription_results.append({
                    "segment": segment_count,
                    "start_time": start_time,
                    "end_time": end_time,
                    "text": text
                })
            else:
                print(f"片段 {segment_count} 识别失败")

            segment_count += 1

        result_file = os.path.join(config["output_dir"], f"transcription_{timestamp}.json")
        with open(result_file, "w", encoding="utf-8") as f:
            json.dump(transcription_results, f, ensure_ascii=False, indent=2)

        text_file = os.path.join(config["output_dir"], f"transcription_{timestamp}.txt")
        with open(text_file, "w", encoding="utf-8") as f:
            f.write(combined_text)

        print(f"所有处理完成，共处理 {segment_count - 1} 个片段")
        print(f"结果已保存到:\n{text_file}")

        return combined_text.strip()

    except Exception as e:
        print(f"处理过程中发生错误: {str(e)}")
        return None


# ----------------------------
# AI 校正函数
# ----------------------------
def get_ai(question, system_prompt='请对以下由语音识别生成的文本进行校正，仅修正识别错误的词汇，保持原文内容、语义和表达风格不变。不要重新组织句子或修改原意，仅针对明显因语音识别导致的错词、错字进行修正，确保文本准确还原语音本意。', base_url='https://api-inference.modelscope.cn/v1', api_key='ms-cc24cb3c-650c-4210-940d-c6007856adf2', model='Qwen/Qwen3-Next-80B-A3B-Instruct'):
    client = OpenAI(
        base_url=base_url,
        api_key=api_key,
        http_client=httpx.Client(verify=False)  # 直接禁用验证
    )

    prompt = question

    response = client.chat.completions.create(
        #model='Qwen/Qwen2.5-Coder-32B-Instruct',
        model=model,
        messages=[
            {'role': 'system', 'content': system_prompt},
            {'role': 'user', 'content': prompt}
        ],
        stream=False
    )

    return response.choices[0].message.content


# ----------------------------
# FastAPI 应用
# ----------------------------
app = FastAPI()


def abc(file_path: str) -> str:
    """处理MP4文件"""
    if not CONFIG:
        return "配置加载失败，无法处理文件。"
    return process_audio_sequentially(file_path, CONFIG)


def get_html_content(result: str = None) -> str:
    """生成HTML页面"""
    
    result_html = ""
    if result:
        # 转义特殊字符，防止XSS
        escaped_result = result.replace("<", "&lt;").replace(">", "&gt;")
        result_html = f"""
        <div class="result-box">
            <h3>原始识别结果:</h3>
            <pre id="original-text">{escaped_result}</pre>
            <button id="ai-correct-btn" onclick="aiCorrect()" class="submit-btn" style="margin-top: 10px;">✨ AI 校正</button>
        </div>
        <div id="ai-result-placeholder"></div>
        <script>
            async function aiCorrect() {{
                const originalText = document.getElementById('original-text').textContent;
                // 显示加载状态
                const btn = document.getElementById('ai-correct-btn');
                btn.disabled = true;
                btn.textContent = '校正中...';
                
                try {{
                    const response = await fetch('/ai_correct', {{
                        method: 'POST',
                        headers: {{
                            'Content-Type': 'application/x-www-form-urlencoded',
                        }},
                        body: 'text=' + encodeURIComponent(originalText)
                    }});

                    const resultHtml = await response.text();
                    document.getElementById('ai-result-placeholder').innerHTML = resultHtml;
                }} catch (error) {{
                    console.error('AI校正请求失败:', error);
                    document.getElementById('ai-result-placeholder').innerHTML = '<div class=\"result-box\" style=\"color: red;\"><h3>请求失败</h3><p>网络错误或服务器无响应</p></div>';
                }} finally {{
                    btn.disabled = false;
                    btn.textContent = '✨ AI 校正';
                }}
            }}
        </script>
        """
    else:
        result_html = ""

    return f"""
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>MP4文件上传处理</title>
        <style>
            body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; line-height: 1.6; }}
            .container {{ text-align: center; padding: 40px 20px; box-shadow: 0 0 10px rgba(0,0,0,0.1); border-radius: 8px; }}
            .upload-form {{ margin: 30px 0; padding: 20px; border: 2px dashed #ccc; border-radius: 6px; }}
            .file-input {{ margin: 15px 0; padding: 10px; }}
            .submit-btn {{ padding: 10px 20px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }}
            .submit-btn:hover {{ background-color: #218838; }}
            .result-box {{ background-color: #f5f5f5; border-radius: 6px; padding: 20px; margin: 20px 0; text-align: left; }}
            pre {{ white-space: pre-wrap; word-wrap: break-word; }}
            button:disabled {{
                background-color: #cccccc !important;
                cursor: not-allowed;
            }}
        </style>
    </head>
    <body>
        <div class="container">
            <h1>视频MP4提取文字</h1>
            <div class="upload-form">
                <form action="/" method="post" enctype="multipart/form-data">
                    <input type="file" name="file" accept=".mp4" class="file-input" required>
                    <br>
                    <button type="submit" class="submit-btn">上传并处理，处理有点慢，请耐心等待</button>
                </form>
            </div>
            {result_html}
        </div>
    </body>
    </html>
    """


@app.get("/", response_class=HTMLResponse)
async def main():
    return get_html_content()


@app.post("/", response_class=HTMLResponse)
async def upload_file(file: UploadFile = File(...)):
    if not file.filename.endswith(".mp4"):
        return get_html_content(f"错误: 请上传MP4格式的文件，当前文件: {file.filename}")

    try:
        with NamedTemporaryFile(delete=False, suffix=".mp4") as temp_file:
            shutil.copyfileobj(file.file, temp_file)
            temp_file_path = temp_file.name

        result = abc(temp_file_path)
        return get_html_content(result)

    except Exception as e:
        return get_html_content(f"处理文件时发生错误: {str(e)}")

    finally:
        if 'temp_file_path' in locals() and os.path.exists(temp_file_path):
            try:
                os.remove(temp_file_path)
            except Exception as e:
                print(f"删除临时文件失败: {str(e)}")


# ----------------------------
# 新增：AI校正路由
# ----------------------------
@app.post("/ai_correct", response_class=HTMLResponse)
async def ai_correct(text: str = Form(...)):
    try:
        corrected_text = get_ai(text,base_url=CONFIG['api_url'],api_key=CONFIG['api_key'],model=CONFIG['model_name2'])
        return f"""
        <div class="result-box">
            <h3>AI 校正结果:</h3>
            <pre>{corrected_text}</pre>
        </div>
        """
    except Exception as e:
        return f"""
        <div class="result-box" style="color: red;">
            <h3>AI 校正失败</h3>
            <p>{str(e)}</p>
        </div>
        """


# ----------------------------
# 启动服务
# ----------------------------
if __name__ == "__main__":
    import uvicorn
    print("当前工作目录:", os.getcwd())
    if CONFIG:
        uvicorn.run(app, host="0.0.0.0", port=8000)
    else:
        print("启动失败：配置加载错误。")



